<template>
  <div ref="domRef" class="dom-wrapper"></div>
</template>

<script setup lang="ts">
import * as THREE from 'three';
import { onMounted, ref } from 'vue';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
let scene: THREE.Scene;
let camera: THREE.PerspectiveCamera;
let renderer: THREE.WebGLRenderer;
let controls: OrbitControls;
let cube: THREE.Mesh;
const domRef = ref(null);
const clock = new THREE.Clock();
const init = (warpDom: HTMLElement) => {
  ///////////
  // SCENE //
  ///////////
  scene = new THREE.Scene();

  ////////////
  // CAMERA //
  ////////////
  const SCREEN_WIDTH = warpDom.clientWidth;
  const SCREEN_HEIGHT = warpDom.clientHeight;
  // camera attributes
  const VIEW_ANGLE = 45,
    ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
    NEAR = 0.1,
    FAR = 20000;
  // set up camera
  camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
  // add the camera to the scene
  scene.add(camera);

  // the camera defaults to position (0,0,0)
  //so pull it back (z = 400) and up (y = 100) and set the angle towards the scene origin
  camera.position.set(0, 150, 400);
  camera.lookAt(scene.position);

  //////////////
  // RENDERER //
  //////////////

  // create and start the renderer; choose antialias setting.
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

  // attach renderer to the wrapper div
  warpDom.appendChild(renderer.domElement);

  //////////////
  // CONTROLS //
  //////////////

  // move mouse and: left   click to rotate,
  //                 middle click to zoom,
  //                 right  click to pan
  controls = new OrbitControls(camera, renderer.domElement);

  ///////////
  // LIGHT 光线 //
  ///////////

  // create a light
  const light = new THREE.PointLight(0xffffff);
  light.position.set(0, 250, 0);
  scene.add(light);

  //////////////
  // GEOMETRY 球体和正方体 //
  //////////////

  // most objects displayed are a "mesh":
  //  a collection of points ("geometry") and
  //  a set of surface parameters ("material")

  // Sphere parameters: radius, segments along width, segments along height
  const sphereGeometry = new THREE.SphereGeometry(50, 32, 16);
  // use a "lambert" material rather than "basic" for realistic lighting.
  //   (don't forget to add (at least one) light!)
  const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0x8888ff });
  const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
  sphere.position.set(100, 50, -50);
  scene.add(sphere);

  const cubeMaterialArray = [];
  // order to add materials: x+,x-,y+,y-,z+,z-
  cubeMaterialArray.push(new THREE.MeshBasicMaterial({ color: 0xff3333 }));
  cubeMaterialArray.push(new THREE.MeshBasicMaterial({ color: 0xff8800 }));
  cubeMaterialArray.push(new THREE.MeshBasicMaterial({ color: 0xffff33 }));
  cubeMaterialArray.push(new THREE.MeshBasicMaterial({ color: 0x33ff33 }));
  cubeMaterialArray.push(new THREE.MeshBasicMaterial({ color: 0x3333ff }));
  cubeMaterialArray.push(new THREE.MeshBasicMaterial({ color: 0x8833ff }));
  // Cube parameters: width (x), height (y), depth (z),
  //        (optional) segments along x, segments along y, segments along z
  const cubeGeometry = new THREE.BoxGeometry(100, 100, 100, 1, 1, 1);
  // using THREE.MeshFaceMaterial() in the constructor below
  //   causes the mesh to use the materials stored in the geometry
  cube = new THREE.Mesh(cubeGeometry, cubeMaterialArray);
  cube.position.set(-100, 50, -50);
  scene.add(cube);

  ///////////
  // FLOOR - 黑白地板 //
  ///////////

  // note: 4x4 checkboard pattern scaled so that each square is 25 by 25 pixels.
  const floorTexture = new THREE.TextureLoader().load('images/checkerboard.jpg');
  floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
  floorTexture.repeat.set(10, 10);
  // DoubleSide: render texture on both sides of mesh
  const floorMaterial = new THREE.MeshBasicMaterial({ map: floorTexture, side: THREE.DoubleSide });
  const floorGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1);
  const floor = new THREE.Mesh(floorGeometry, floorMaterial);
  floor.position.y = -0.5;
  floor.rotation.x = Math.PI / 2;
  scene.add(floor);

  /////////
  // SKY -  天空盒 //
  /////////

  // recommend either a skybox or fog effect (can't use both at the same time)
  // without one of these, the scene's background color is determined by webpage background

  // make sure the camera's "far" value is large enough so that it will render the skyBox!
  const skyBoxGeometry = new THREE.BoxGeometry(10000, 10000, 10000);
  // var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
  // BackSide: render faces from inside of the cube, instead of from outside (default).
  const skyBoxMaterial = new THREE.MeshBasicMaterial({ color: 0x9999ff, side: THREE.BackSide });
  const skyBox = new THREE.Mesh(skyBoxGeometry, skyBoxMaterial);
  scene.add(skyBox);
  
  scene.fog = new THREE.FogExp2(0x9999ff, 0.00025);
};
const render = () => {
  renderer.render(scene, camera);
};
const update = () => {
  // delta = change in time since last call (in seconds)
  const delta = clock.getDelta();

  controls.update();
};
const animate = () => {
  requestAnimationFrame(animate);
  render();
  update();
};
onMounted(() => {
  if (domRef.value) {
    init(domRef.value);
    animate();
  }
});
</script>

<style scoped>
.dom-wrapper {
  width: 100%;
  height: 100%;
}
</style>
